package com.xishu.util;

import org.apache.commons.lang.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;

import javax.persistence.Tuple;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;

public class NativeResultProcessUtils {
    private static Logger logger = LoggerFactory.getLogger(NativeResultProcessUtils.class);


    /**
     * tuple转实体对象
     *
     * @param source      tuple对象
     * @param targetClass 目标实体class
     * @param <T>         目标实体类型
     * @return 目标实体
     */
    public static <T> T processResult(Tuple source, Class<T> targetClass) {
        Object instantiate = BeanUtils.instantiate(targetClass);
        convertTupleToBean(source, instantiate, null);
        return (T) instantiate;
    }

    /**
     * tuple转实体对象
     *
     * @param source           tuple对象
     * @param targetClass      目标实体class
     * @param <T>              目标实体类型
     * @param ignoreProperties 要忽略的属性
     * @return 目标实体
     */
    public static <T> T processResult(Tuple source, Class<T> targetClass, String... ignoreProperties) {
        Object instantiate = BeanUtils.instantiate(targetClass);
        convertTupleToBean(source, instantiate, ignoreProperties);
        return (T) instantiate;
    }

    /**
     * 把tuple中属性名相同的值复制到实体中
     *
     * @param source tuple对象
     * @param target 目标对象实例
     */
    public static void convertTupleToBean(Tuple source, Object target) {
        convertTupleToBean(source, target, null);
    }

    /**
     * 把tuple中属性名相同的值复制到实体中
     *
     * @param source           tuple对象
     * @param target           目标对象实例
     * @param ignoreProperties 要忽略的属性
     */
    public static void convertTupleToBean(Tuple source, Object target, String... ignoreProperties) {
        //目标class
        Class<?> actualEditable = target.getClass();
        //获取目标类的属性信息
        PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
        //忽略列表
        List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

        //遍历属性节点信息
        for (PropertyDescriptor targetPd : targetPds) {
            //获取set方法
            Method writeMethod = targetPd.getWriteMethod();
            //判断字段是否可以set
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                //获取source节点对应的属性
                String propertyName = targetPd.getName();

                Object value = null;
                try {
                    value = source.get(propertyName);
                } catch (Exception e) {
                    //不存在，可以忽略不处理
                    continue;
                }

                if (value != null && ClassUtil.isAssignable(writeMethod.getParameterTypes()[0], value.getClass())) {
                    try {
                        //判断target属性是否private
                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                            writeMethod.setAccessible(true);
                        }

                        value = getValue(writeMethod.getParameterTypes()[0], value);

                        //写入target
                        writeMethod.invoke(target, value);
                    } catch (Throwable ex) {
                        logger.error("e", ex);
                        continue;
//                        throw new FatalBeanException(
//                                "Could not copy property '" + targetPd.getName() + "' from source to target", ex);

                    }
                } else if ("value".equals(propertyName) && value != null) {
                    //只要是value值，也就是报表，不为空的时候，都写到value里面去,那么一定是整数
                    writeMethod.setAccessible(true);
                    try {
                        double valueDouble = ((Number) value).doubleValue();
                        BigDecimal b =  BigDecimal.valueOf(valueDouble);
                        valueDouble = b.setScale(0, BigDecimal.ROUND_HALF_UP).doubleValue();
                        writeMethod.invoke(target, valueDouble);
                    } catch (Throwable e) {
                        throw new FatalBeanException(
                                "Could not copy property '" + targetPd.getName() + "' from source to target", e);
                    }
                } else if ("xTime".equals(propertyName) && value != null) {
                    //写横坐标
                    writeMethod.setAccessible(true);
                    try {
                        double valueDouble = ((Number) value).doubleValue();
                        BigDecimal b =  BigDecimal.valueOf(valueDouble);
                        valueDouble = b.setScale(0, BigDecimal.ROUND_HALF_UP).doubleValue();
                        writeMethod.invoke(target, valueDouble);
                    } catch (Throwable e) {
                        throw new FatalBeanException(
                                "Could not copy property '" + targetPd.getName() + "' from source to target", e);
                    }
                }
            }
        }
    }

    /**
     * 获取值
     *
     * @param targetType
     * @param value
     * @return
     */
    public static Object getValue(Class targetType, Object value) {
        if (targetType.getTypeName().equalsIgnoreCase(value.getClass().getTypeName())) {
            return value;
        }

        if (targetType.getTypeName().equalsIgnoreCase("java.lang.Long") && value.getClass().getTypeName().equals("java.math.BigInteger")) {
            return ((BigInteger) value).longValue();
        }

        if (targetType.getTypeName().equalsIgnoreCase("java.lang.Integer") && value.getClass().getTypeName().equals("java.math.BigInteger")) {
            return ((BigInteger) value).intValue();
        }

        if (targetType.getTypeName().equalsIgnoreCase("java.lang.Long") && value.getClass().getTypeName().equals("java.lang.Integer")) {
            return ((Integer) value).longValue();
        }

        return value;
    }

}